/*
 * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
 * Adaptations to ESP-IDF Copyright (c) 2016-2018 Espressif Systems (Shanghai) PTE LTD
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include "sdmmc_common.h"

#define SDMMC_INIT_STEP(condition, function) \
    do { \
        if ((condition)) { \
            sdmmc_err_t err = (function)(card); \
            if (err != SDMMC_OK) { \
                SDMMC_LOGE("%s: %s returned 0x%x\n", __func__, #function, err); \
                return err; \
            } else {\
                SDMMC_LOGI("%s: %s success\n", __func__, #function);\
            }\
        } \
    } while(0);


sdmmc_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card)
{
    memset(card, 0, sizeof(*card));
    memcpy(&card->host, config, sizeof(*config));
    const bool is_spi = host_is_spi(card);
    const bool always = true;
    const bool io_supported = true;

    /* 检查主机标志与接口配置是否兼容. */
    SDMMC_INIT_STEP(!is_spi, sdmmc_fix_host_flags);

    /* 在重新初始化IO卡之前使用SD_IO_RW_DIRECT (CMD52)命令重置SDIO. */
    SDMMC_INIT_STEP(io_supported, sdmmc_io_reset);

    /* 使用GO_IDLE_STATE (CMD0)命令重置卡. */
    SDMMC_INIT_STEP(always, sdmmc_send_cmd_go_idle_state);

    /* 使用SEND_IF_COND (CMD8)命令用于识别SDHC/SDXC卡. */
    SDMMC_INIT_STEP(always, sdmmc_init_sd_if_cond);

    /* 使用IO_SEND_OP_COND(CMD5)命令判断该卡是否是IO卡. */
    SDMMC_INIT_STEP(io_supported, sdmmc_init_io);

    const bool is_mem = card->is_mem;
    const bool is_sdio = !is_mem;

    /* 为SPI模式下的数据传输启用CRC16检查. */
    SDMMC_INIT_STEP(is_spi, sdmmc_init_spi_crc);

    /* 使用SEND_OP_COND (SD卡为ACMD41; MMC卡为CMD1)命令设置卡OCR. */
    SDMMC_INIT_STEP(is_mem, sdmmc_init_ocr);

    const bool is_mmc = is_mem && card->is_mmc;
    const bool is_sdmem = is_mem && !is_mmc;

    SDMMC_LOGD("%s: card type is %s\n", __func__, is_sdio ? "SDIO" : is_mmc ? "MMC" : "SD");

    /* 使用MMC_ALL_SEND_CID (CMD2)命令读取CID寄存器的值. */
    SDMMC_INIT_STEP(is_mem, sdmmc_init_cid);

    /* 使用SD_SEND_RELATIVE_ADDR (CMD3)命令设置RCA. */
    SDMMC_INIT_STEP(!is_spi, sdmmc_init_rca);

    /* 使用MMC_SEND_CSD (CMD9)命令读取CSD寄存器的值. */
    SDMMC_INIT_STEP(is_mem, sdmmc_init_csd);

    /* 解码MMC的CID寄存器的内容. */
    SDMMC_INIT_STEP(is_mmc && !is_spi, sdmmc_init_mmc_decode_cid);

    /*
     * 使用MMC_SELECT_CARD (CMD7)命令将卡从待机模式切换到数据传输模式(如果使用SPI接口则不需要切换).
     * 这是发出SET_BLOCKLEN和SEND_SCR命令所需要的.
     */
    SDMMC_INIT_STEP(!is_spi, sdmmc_init_select_card);

    /* SD存储卡: 使用MMC_SET_BLOCKLEN ()命令设置SDSC卡的块大小为512字节(与SDHC相同). */
    SDMMC_INIT_STEP(is_sdmem, sdmmc_init_sd_blocklen);
    /* SD存储卡: 使用SD_APP_SEND_SCR (CMD51)命令读取SCR寄存器的内容. */
    SDMMC_INIT_STEP(is_sdmem, sdmmc_init_sd_scr);
    /* SD存储卡: 使用MMC_SEND_STATUS (CMD13)命令等待进入数据传输状态. */
    SDMMC_INIT_STEP(is_sdmem, sdmmc_init_sd_wait_data_ready);

    /* MMC卡: 使用MMC_SEND_EXT_CSD (CMD8)命令读取CXD寄存器的内容. */
    SDMMC_INIT_STEP(is_mmc, sdmmc_init_mmc_read_ext_csd);

    /*
     * 如果卡支持HS模式, 使用(SD和MMC卡为CMD6; SDIO为CMD52)命令尝试切换到HS模式.
     * 设置相应的card->max_freq_khz值.
     */
    SDMMC_INIT_STEP(always, sdmmc_init_card_hs_mode);

    /* 设置总线宽度. 每一种卡都有一次调用, 然后是主机的一次调用. */
    if (!is_spi) {
        /* SD存储卡: 使用SD_APP_SET_BUS_WIDTH (CMD6)命令设置总线宽度. */
        SDMMC_INIT_STEP(is_sdmem, sdmmc_init_sd_bus_width);
        /* SDIO: 使用SD_IO_RW_DIRECT (CMD52)命令设置总线宽度. */
        SDMMC_INIT_STEP(is_sdio, sdmmc_init_io_bus_width);
        /* MMC卡: 使用MMC_SWITCH (CMD6)命令设置总线宽度. */
        SDMMC_INIT_STEP(is_mmc, sdmmc_init_mmc_bus_width);
        SDMMC_INIT_STEP(always, sdmmc_init_host_bus_width);
    }

    /* SD存储卡: 使用SD_APP_SD_STATUS (ACMD13)命令读取SD卡状态寄存器. */
    SDMMC_INIT_STEP(is_sdmem, sdmmc_init_sd_ssr);

    /* 使用card->max_freq_khz切换主机使用频率. */
    SDMMC_INIT_STEP(always, sdmmc_init_host_frequency);

    /* SD存储卡: 切换总线模式和频率后的完整性检查. */
    SDMMC_INIT_STEP(is_sdmem, sdmmc_check_scr);

    /* MMC卡: EMMC切换到HS模式后的完整性检查. */
    SDMMC_INIT_STEP(is_mmc, sdmmc_init_mmc_check_ext_csd);

    /* TODO: 为SDIO添加类似的检查. */

    return SDMMC_OK;
}
